/*LED平台驱动程序*/

/*
	myled_ctrl {																											
		status = "okay";																										
		compatible = "rk3399_led2";																										
		gpios1 = <&gpio1 23 GPIO_ACTIVE_HIGH>; //GPIO1_C7
		gpios2 = <&gpio1 24 GPIO_ACTIVE_HIGH>; //GPIO1_D0
	};
*/

#include <linux/module.h>
#include <linux/init.h>
#include <linux/fs.h>
#include <linux/device.h>
#include <linux/slab.h>
#include <linux/gpio.h>
#include <linux/string.h>
#include <asm/uaccess.h>
#include <linux/interrupt.h>
#include <linux/gpio.h>
#include <linux/platform_device.h>
#include <linux/of.h>
#include <linux/of_gpio.h>
#include <dt-bindings/gpio/gpio.h>

#include "led.h"


/*定义结构体类型*/
struct rk3399_led
{
	unsigned int major;
	dev_t dev;
	struct class* pcls;
	struct device* pdev;
	unsigned int gpio1,gpio2;//分别对应led_green和led2_orange的引脚号
	int value;
	int validvalue;
};

/*全局变量*/
static struct rk3399_led* pled;//声明结构体指针，但没分配结构体变量空间

//定义id_table表
static const struct platform_device_id led_id_table[] = 
{
	{ .name = "rk3399_led", .driver_data = 0x1 }, 
	{ .name = "rk3288_led", .driver_data = 0x2 }, 
	{ .name = "rk3568_led", .driver_data = 0x3 },
};

//定义compatible匹配表
static const struct of_device_id led_match[] = 
{
	{ .compatible = "rk3399_led2", },
};


/*文件操作函数*/
static ssize_t led_read(struct file *filp, char __user *buf, size_t size, loff_t *flag)
{
	int ret;
	//printk("<kernel>call function:%s\n",__FUNCTION__);

	pled->value = gpio_get_value(pled->gpio1);
	ret = copy_to_user(buf, &pled->value, size);
	if(ret > 0)
	{
		printk("<kernel>copy_to_user fail.\n");
		return -EFAULT;
	}	
	
	return sizeof(int);
}

static ssize_t led_write(struct file *filp, const char __user *buf, size_t size, loff_t *flag)
{
	int ret;
	printk("<kernel>call function:%s\n",__FUNCTION__);

	ret = copy_from_user(&pled->value, buf, size);
	if(ret > 0)
	{
		printk("<kernel>copy_from_user fail.\n");
		return -EFAULT;
	}
	
	if(pled->value == 1)
	{
		printk("<kernel>LED1 ON!\n");
		gpio_set_value(pled->gpio1, 1);//开灯
	}
	else if(pled->value == 0)
	{
		printk("<kernel>LED1 OFF!\n");
		gpio_set_value(pled->gpio1, 0);//关灯
	}
	
	return size;
}

static long led_ioctl(struct file * filp, unsigned int cmd, unsigned long arg)
{
	//int ret;
	//DATA udata;//用户空间发送过来的数据
	//static DATA kdata = {'k',0};//要发送给用户空间的数据

	/*解析命令*/
	if(_IOC_TYPE(cmd) != 'L')
	{
		printk("<kernel>NOT LED COMMAND\n");
		return -EFAULT;
	}

	/*执行命令*/
	switch (cmd) 
	{
		case LED_ON:
			if (arg != 1 && arg != 2)
				return -EFAULT;
			else if (arg == 1)
			{
				printk("<kernel>LED1 ON.\n");
				gpio_set_value(pled->gpio1, 1);//开LED1
			}
			else if (arg == 2)
			{
				printk("<kernel>LED2 ON.\n");
				gpio_set_value(pled->gpio2, 1);//开LED2
			}
			break;
		case LED_OFF:
			if (arg != 1 && arg != 2)
				return -EFAULT;
			else if (arg == 1)
			{
				printk("<kernel>LED1 OFF.\n");
				gpio_set_value(pled->gpio1, 0);//关LED1
			}
			else if (arg == 2)
			{
				printk("<kernel>LED2 OFF.\n");
				gpio_set_value(pled->gpio2, 0);//关LED2
			}
			break;
		case LED_ALL_ON:
			printk("<kernel>LED ALL ON.\n");
			gpio_set_value(pled->gpio1, 1);//开LED1
			gpio_set_value(pled->gpio2, 1);//开LED2
			break;
		case LED_ALL_OFF:
			printk("<kernel>LED ALL OFF.\n");
			gpio_set_value(pled->gpio1, 0);//关LED1
			gpio_set_value(pled->gpio2, 0);//关LED2
			break;
			
		default:
			printk("<kernel>UNKNOWN COMMAND.\n");
			break;
	}

	return 0;
}


/*改变系统调用接口地址*/
static struct file_operations fops =
{
	.owner = THIS_MODULE,
	.read = led_read,
	.write = led_write,
	.unlocked_ioctl = led_ioctl,
};

/*定义probe、remove函数*/
static int led_probe(struct platform_device *pdev)
{
	int ret;
	enum of_gpio_flags flags;
	struct device_node* pled_node = NULL;
	printk("<kernel>call function:%s\n",__FUNCTION__);

	/*获取平台数据*/
	pled_node = pdev->dev.of_node;

	/*申请分配结构体空间*/
	pled = kmalloc(sizeof(struct rk3399_led), GFP_KERNEL);
	if(pled == NULL)
	{
		printk("kmalloc fail\n");
		return -ENOMEM;
	}

	/*注册主设备号（动态）*/
	pled->major = register_chrdev(0, "led_drv", &fops);
	if(pled->major < 0)
	{
		printk("<kernel>register major fail.\n");
		ret = -EBUSY;
		goto err_register_chrdev;
	}
	printk("<kernel>register major success!\n");
	printk("<kernel>major = %d\n",pled->major);

	/*创建设备类*/
	pled->pcls = class_create(THIS_MODULE, "led_cls");
	if (IS_ERR(pled->pcls))
	{
		ret = PTR_ERR(pled->pcls);
		goto err_class_create;
	}

	/*创建设备文件*/
	pled->pdev = device_create(pled->pcls,NULL, MKDEV(pled->major, 123),NULL,"led");
	if (IS_ERR(pled->pdev))
	{
		ret = PTR_ERR(pled->pdev);
		goto err_device_create;
	} 

	/*获取LED引脚号*/
	pled->gpio1 = of_get_named_gpio_flags(pled_node, "gpios1", 0, &flags);
	pled->gpio2 = of_get_named_gpio_flags(pled_node, "gpios2", 0, &flags);
	if (gpio_is_valid(pled->gpio1)) //如果获取到的引脚号是有效的
	{
		printk("<kernel>Get gpio of led1 success!\n");
		printk("<kernel>gpio_led1 = %d\n", pled->gpio1);
	} 
	else 
	{
		printk("<kernel>of_get_named_gpio_flags fail.\n");
		return -EFAULT;
	}	
	if (gpio_is_valid(pled->gpio2)) //如果获取到的引脚号是有效的
	{
		printk("<kernel>Get gpio of led2 success!\n");
		printk("<kernel>gpio_led2 = %d\n", pled->gpio2);
	} 
	else 
	{
		printk("<kernel>of_get_named_gpio_flags fail.\n");
		return -EFAULT;
	}		

	/*获取有效电平*/
	if(flags == GPIO_ACTIVE_LOW)
	{
		printk("<kernel>Low is valid.\n");
		pled->validvalue = 0;
	}
	else
	{
		printk("<kernel>High is valid.\n");
		pled->validvalue = 1;
	}

	/*设置IO口方向*/
	gpio_request(pled->gpio1, "led2_green");
	gpio_request(pled->gpio2, "led2_orange");
	gpio_direction_output(pled->gpio1, !pled->validvalue);//将IO口设置为输出方向，初始为关灯状态
	gpio_direction_output(pled->gpio2, !pled->validvalue);//将IO口设置为输出方向，初始为关灯状态
	gpio_free(pled->gpio1);
	gpio_free(pled->gpio2);

	return 0;

	/*集中容错处理*/
err_device_create:
	class_destroy(pled->pcls);
err_class_create:
	unregister_chrdev(pled->major, "led_drv");
err_register_chrdev:
	kfree(pled); 

	return ret;
}

static int led_remove(struct platform_device *pdev)
{
	printk("<kernel>call function:%s\n",__FUNCTION__);

	/*关灯*/
	gpio_set_value(pled->gpio1, !pled->validvalue);
	gpio_set_value(pled->gpio2, !pled->validvalue);

	/*销毁设备文件*/
	device_destroy(pled->pcls, MKDEV(pled->major, 123));

	/*销毁设备类*/
	class_destroy(pled->pcls);
	
	/*注销主设备号*/
	unregister_chrdev(pled->major, "led_drv");
	printk("<kernel>unregister major success!\n");

	/*释放堆区空间*/
	kfree(pled);

	return 0;
}

/*定义平台驱动对象*/	
static struct platform_driver led_driver = 
{
	.probe		= led_probe,
	.remove		= led_remove,
	.driver		= {.name	= "rk_led", .of_match_table = led_match,},
	.id_table	= led_id_table,
};


/*入口函数*/
static int __init driver_mod_init(void)
{
	printk("<kernel>call function:%s\n",__FUNCTION__);
	return platform_driver_register(&led_driver);
}

/*出口函数*/
static void __exit driver_mod_exit(void)
{
	printk("<kernel>call function:%s\n",__FUNCTION__);
	platform_driver_unregister(&led_driver);
}



module_init(driver_mod_init); //告诉内核本模块程序的入口函数名
module_exit(driver_mod_exit);//告诉内核本模块程序的出口函数名
MODULE_LICENSE("GPL"); //遵守开源许可协议



